﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;

namespace ProgNetComponentsDx.Tasks
{
    /// <summary>
    /// BaseTask
    /// </summary>
    public abstract class BaseTask
    {
        public TaskScheduler Scheduler
        {
            get;
            set;
        }
        /// <summary>
        /// Initializes the <see cref="BaseTask"/> class.
        /// </summary>
        static BaseTask()
        {
            OperationInProgressMessage = "Operacja w toku.";
            ValidationFailedMessage = "Błąd walidacji.";
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="BaseTask"/> class.
        /// </summary>
        protected BaseTask()
        {
            Exceptions = new List<Exception>();
            CancellationToken = new CancellationTokenSource();
        }
        /// <summary>
        /// Occurs when task is finished.
        /// </summary>
        public event EventHandler TaskFinished;

        /// <summary>
        /// Gets the cancellation token.
        /// </summary>
        /// <value>
        /// The cancellation token.
        /// </value>
        protected CancellationTokenSource CancellationToken
        {
            get;
            private set;
        }
        /// <summary>
        /// Gets the exceptions.
        /// </summary>
        /// <value>
        /// The exceptions.
        /// </value>
        protected List<Exception> Exceptions
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the operation in progress message.
        /// </summary>
        /// <value>
        /// The operation in progress message.
        /// </value>
        public static string OperationInProgressMessage
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the validation failed message.
        /// </summary>
        /// <value>
        /// The validation failed message.
        /// </value>
        public static string ValidationFailedMessage
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the status.
        /// </summary>
        /// <value>
        /// The status.
        /// </value>
        public TaskStatus Status
        {
            get;
            protected set;
        }
        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>
        /// The name.
        /// </value>
        public string Name
        {
            get;
            set;
        }
        /// <summary>
        /// Gets a value indicating whether [is in background].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [is in background]; otherwise, <c>false</c>.
        /// </value>
        public bool IsInBackground
        {
            get;
            private set;
        }
        /// <summary>
        /// Finishes the with status.
        /// </summary>
        /// <param name="status">The status.</param>
        protected virtual void FinishWithStatus(TaskStatus status)
        {
            Status = status;
            if (TaskFinished != null)
                TaskFinished(this, EventArgs.Empty);
        }
        /// <summary>
        /// Runs the specified in separate task.
        /// </summary>
        /// <param name="inBackground">if set to <c>false</c> task will run Synchronously.</param>
        public void Run(bool inBackground = true)
        {
            if (CanRunTask())
            {
                try
                {
                    PrepareTaskToPerform();
                    Status = TaskStatus.Running;
                    IsInBackground = inBackground;
                    if (Scheduler == null)
                    {
                        try
                        {
                            Scheduler = TaskScheduler.FromCurrentSynchronizationContext();
                        }catch{}
                    }
                    Task<TaskStatus> t = null;
                    if (inBackground)
                    {
                        t = Task.Factory.StartNew<TaskStatus>(PerformTask, CancellationToken.Token);
                        t.ContinueWith(FinishTask, Scheduler);
                    }
                    else
                    {
                        t = new Task<TaskStatus>(PerformTask, CancellationToken.Token);
                        t.RunSynchronously(Scheduler);
                        FinishTask(t);
                    }
                }
                catch (Exception ex)
                {
                    AddException(ex);
                    FinishWithStatus(TaskStatus.CanceledByException);
                }
            }
        }
        /// <summary>
        /// Cancels performing task.
        /// </summary>
        public void Cancel()
        {
            if (IsTaskRunning())
                CancellationToken.Cancel();
        }
        /// <summary>
        /// Determines whether this instance [can run task].
        /// </summary>
        /// <returns></returns>
        public virtual bool CanRunTask()
        {
            if (IsTaskRunning())
            {
                ShowOperationInProgressMessage();
                return false;
            }
            else
            {
                if (!ValidateParameters())
                {
                    ShowValidationFailedMessage();
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// Finishes the task.
        /// </summary>
        /// <param name="task">The task.</param>
        protected virtual void FinishTask(Task<TaskStatus> task)
        {
            FinishWithStatus(task.Result);
        }
        /// <summary>
        /// Shows the operation in progress message.
        /// </summary>
        public virtual void ShowOperationInProgressMessage()
        {
            string caption = Application.ProductName;
            if (Name.IsNotNull())
                caption += " [" + Name + "]";
            DxFormExt.ShowDxInfoDialog(null, OperationInProgressMessage, caption);
        }
        /// <summary>
        /// Shows the validation failed message.
        /// </summary>
        public virtual void ShowValidationFailedMessage()
        {
            string caption = Application.ProductName;
            if (Name.IsNotNull())
                caption += " [" + Name + "]";
            DxFormExt.ShowDxInfoDialog(null, ValidationFailedMessage, caption);
        }
        /// <summary>
        /// Performs the task.
        /// </summary>
        /// <returns></returns>
        protected virtual TaskStatus PerformTask()
        {
            return PerformTask(CancellationToken.Token);
        }
        /// <summary>
        /// Performs the task.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        protected abstract TaskStatus PerformTask(CancellationToken token);
        /// <summary>
        /// Prepares the task to perform.
        /// </summary>
        protected virtual void PrepareTaskToPerform()
        {
            Exceptions.Clear();
            if (CancellationToken != null)
                CancellationToken.Dispose();
            CancellationToken = new CancellationTokenSource();
        }
        /// <summary>
        /// Determines whether [is task running].
        /// </summary>
        /// <returns></returns>
        public virtual bool IsTaskRunning()
        {
            return Status == TaskStatus.Running;
        }
        /// <summary>
        /// Validates the parameters.
        /// </summary>
        /// <returns></returns>
        protected virtual bool ValidateParameters()
        {
            return true;
        }
        /// <summary>
        /// Resets the status.
        /// </summary>
        public void ResetStatus()
        {
            if (!IsTaskRunning())
                Status = TaskStatus.None;
        }
        /// <summary>
        /// Adds exception to list of exceptions
        /// </summary>
        /// <param name="ex"></param>
        public void AddException(Exception ex)
        {
            Exceptions.Add(ex);
        }
        /// <summary>
        /// Gets exceptions
        /// </summary>
        /// <returns></returns>
        public Exception[] GetExceptions()
        {
            return Exceptions.ToArray();
        }
    }

    /// <summary>
    /// BaseEventTask
    /// </summary>
    public class BaseEventTask : BaseTask
    {
        /// <summary>
        /// Occurs when task is performed.
        /// </summary>
        public event PerformTaskHandler TaskPerformed;

        /// <summary>
        /// Performs the task.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        protected override TaskStatus PerformTask(CancellationToken token)
        {
            var e =  new TaskResultEventArgs(TaskStatus.Finished, token);
            if (TaskPerformed != null)
            {
                try
                {
                    TaskPerformed(this, e);
                }
                catch (OperationCanceledException)
                {
                    e.Result = TaskStatus.Canceled;
                }
                catch (Exception ex)
                {
                    AddException(ex);
                    e.Result = TaskStatus.CanceledByException;
                }
            }
            return e.Result;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public class TaskResultEventArgs : EventArgs
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TaskResultEventArgs"/> class.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <param name="token">The token.</param>
        public TaskResultEventArgs(TaskStatus result, CancellationToken token)
        {
            Result = result;
            Token = token;
        }
        /// <summary>
        /// Gets or sets the result.
        /// </summary>
        /// <value>
        /// The result.
        /// </value>
        public TaskStatus Result
        {
            get;
            set;
        }
        /// <summary>
        /// Gets the cancellation token.
        /// </summary>
        /// <value>
        /// The token.
        /// </value>
        public CancellationToken Token
        {
            get;
            private set;
        }
        /// <summary>
        /// Throws if cancellation requested.
        /// </summary>
        public void ThrowIfCancellationRequested()
        {
            Token.ThrowIfCancellationRequested();
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="TaskResultEventArgs"/> instance containing the event data.</param>
    public delegate void PerformTaskHandler(object sender, TaskResultEventArgs e);

    /// <summary>
    /// 
    /// </summary>
    public enum TaskStatus
    {
        /// <summary>
        /// None
        /// </summary>
        None,
        /// <summary>
        /// Running
        /// </summary>
        Running,
        /// <summary>
        /// Canceled
        /// </summary>
        Canceled,
        /// <summary>
        /// CanceledByException
        /// </summary>
        CanceledByException,
        /// <summary>
        /// Finished
        /// </summary>
        Finished,
        /// <summary>
        /// FinishedWithException
        /// </summary>
        FinishedWithException
    }
}
